builder: Add gtk_builder_create_closure()
authorBenjamin Otte <otte@redhat.com>
Thu, 21 Nov 2019 20:20:16 +0000 (21:20 +0100)
committerBenjamin Otte <otte@redhat.com>
Fri, 22 Nov 2019 06:46:18 +0000 (07:46 +0100)
This will be the future way to connect signals automatically (and be
used for other things, too).

For now, gtk_builder_connect_signals_default() is ported to use it.

docs/reference/gtk/gtk4-sections.txt
gtk/gtkbuilder.c
gtk/gtkbuilder.h

index 04bf68f83b896febe00fde34da13f5a7065ef10f..c9fefcebf14cf13c48d7e2c0a29d8e964afc792e 100644 (file)
@@ -529,6 +529,8 @@ gtk_builder_new_from_string
 gtk_builder_add_callback_symbol
 gtk_builder_add_callback_symbols
 gtk_builder_lookup_callback_symbol
+gtk_builder_create_closure
+gtk_builder_create_cclosure
 gtk_builder_add_from_file
 gtk_builder_add_from_resource
 gtk_builder_add_from_string
index 5538ecfe8c6304e76babb515f45ba4abf86d8fe3..5c00a5057087f52155d92a985458c5c55fb37917 100644 (file)
@@ -1658,31 +1658,23 @@ gtk_builder_connect_signals_default (GtkBuilder    *builder,
                                      GConnectFlags  flags,
                                      gpointer       user_data)
 {
-  GCallback func;
+  GClosure *closure;
+  GError *error = NULL;
 
-  func = gtk_builder_lookup_callback_symbol (builder, handler_name);
+  closure = gtk_builder_create_closure (builder,
+                                        handler_name,
+                                        flags & G_CONNECT_SWAPPED ? TRUE : FALSE,
+                                        connect_object,
+                                        &error);
 
-  if (!func)
+  if (closure == NULL)
     {
-      GModule *module = gtk_builder_get_module (builder);
-
-      /* Only error out for missing GModule support if we've not
-       * found the symbols explicitly added with gtk_builder_add_callback_symbol()
-       */
-      if (module == NULL)
-        g_error ("gtk_builder_connect_signals() requires working GModule");
-
-      if (!g_module_symbol (module, handler_name, (gpointer)&func))
-        {
-          g_warning ("Could not find signal handler '%s'.  Did you compile with -rdynamic?", handler_name);
-          return;
-        }
+      g_warning ("%s", error->message);
+      g_error_free (error);
+      return;
     }
 
-  if (connect_object)
-    g_signal_connect_object (object, signal_name, func, connect_object, flags);
-  else
-    g_signal_connect_data (object, signal_name, func, user_data, NULL, flags);
+  g_signal_connect_closure (object, signal_name, closure, flags & G_CONNECT_AFTER ? TRUE : FALSE);
 }
 
 
@@ -2571,7 +2563,7 @@ _gtk_builder_get_template_type (GtkBuilder *builder)
  *
  * Adds the @callback_symbol to the scope of @builder under the given @callback_name.
  *
- * Using this function overrides the behavior of gtk_builder_connect_signals()
+ * Using this function overrides the behavior of gtk_builder_create_closure()
  * for any callback symbols that are added. Using this method allows for better
  * encapsulation as it does not require that callback symbols be declared in
  * the global namespace.
@@ -2666,6 +2658,122 @@ gtk_builder_lookup_callback_symbol (GtkBuilder  *builder,
   return g_hash_table_lookup (priv->callbacks, callback_name);
 }
 
+static GClosure *
+gtk_builder_create_closure_for_funcptr (GtkBuilder *builder,
+                                        GCallback   callback,
+                                        gboolean    swapped,
+                                        GObject    *object)
+{
+  GClosure *closure;
+
+  if (object)
+    {
+      if (swapped)
+        closure = g_cclosure_new_object_swap (callback, object);
+      else
+        closure = g_cclosure_new_object (callback, object);
+    }
+  else
+    {
+      if (swapped)
+        closure = g_cclosure_new_swap (callback, NULL, NULL);
+      else
+        closure = g_cclosure_new (callback, NULL, NULL);
+    }
+
+  return closure;
+}
+
+/**
+ * gtk_builder_create_closure:
+ * @builder: a #GtkBuilder
+ * @function_name: name of the function to look up
+ * @swapped: %TRUE to create a swapped closure
+ * @object: (nullable): Object to create the closure with
+ * @error: (allow-none): return location for an error, or %NULL
+ *
+ * Creates a closure to invoke the function called @function_name.
+ *
+ * If a closure function was set via gtk_builder_set_closure_func(),
+ * will be invoked.
+ * Otherwise, gtk_builder_create_cclosure() will be called.
+ *
+ * If no closure could be created, %NULL will be returned and @error will
+ * be set.
+ *
+ * Returns: (nullable): A new closure for invoking @function_name
+ **/
+GClosure *
+gtk_builder_create_closure (GtkBuilder *builder,
+                            const char *function_name,
+                            gboolean    swapped,
+                            GObject    *object,
+                            GError    **error)
+{
+  g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (function_name, NULL);
+  g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  return gtk_builder_create_cclosure (builder, function_name, swapped, object, error);
+}
+
+/**
+ * gtk_builder_create_cclosure: (skip)
+ * @builder: a #GtkBuilder
+ * @function_name: name of the function to look up
+ * @swapped: %TRUE to create a swapped closure
+ * @object: (nullable): Object to create the closure with
+ * @error: (allow-none): return location for an error, or %NULL
+ *
+ * This is the default function used by gtk_builder_set_closure_func(). Some bindings
+ * with C support may want to call this function as a fallback from their closure
+ * function.
+ *
+ * This function has no purpose otherwise.
+ *
+ * This function will prefer callbacks added via gtk_builder_add_callback_symbol()
+ * to looking up public symbols.
+ *
+ * Returns: (nullable): A new closure for invoking @function_name
+ **/
+GClosure *
+gtk_builder_create_cclosure (GtkBuilder *builder,
+                             const char *function_name,
+                             gboolean    swapped,
+                             GObject    *object,
+                             GError    **error)
+{
+  GModule *module = gtk_builder_get_module (builder);
+  GCallback func;
+
+  func = gtk_builder_lookup_callback_symbol (builder, function_name);
+  if (func)
+    return gtk_builder_create_closure_for_funcptr (builder, func, swapped, object);
+
+  if (module == NULL)
+    {
+      g_set_error (error,
+                   GTK_BUILDER_ERROR,
+                   GTK_BUILDER_ERROR_INVALID_FUNCTION,
+                   "Could not look up function `%s`: GModule is not supported.",
+                   function_name);
+      return NULL;
+    }
+
+  if (!g_module_symbol (module, function_name, (gpointer)&func))
+    {
+      g_set_error (error,
+                   GTK_BUILDER_ERROR,
+                   GTK_BUILDER_ERROR_INVALID_FUNCTION,
+                   "No function named `%s`.",
+                   function_name);
+      return NULL;
+    }
+
+  return gtk_builder_create_closure_for_funcptr (builder, func, swapped, object);
+}
+
 /**
  * gtk_builder_new_from_file:
  * @filename: filename of user interface description file
index f3353cedcfe209c7a719c2f6b06888b67e7d4c35..fb192dc4a9a9f6b8415ad36f86f7e973297c9bc7 100644 (file)
@@ -63,7 +63,10 @@ typedef struct _GtkBuilderClass   GtkBuilderClass;
  * @GTK_BUILDER_ERROR_TEMPLATE_MISMATCH: The wrong type was specified in a composite class’s template XML
  * @GTK_BUILDER_ERROR_INVALID_PROPERTY: The specified property is unknown for the object class.
  * @GTK_BUILDER_ERROR_INVALID_SIGNAL: The specified signal is unknown for the object class.
- * @GTK_BUILDER_ERROR_INVALID_ID: An object id is unknown
+ * @GTK_BUILDER_ERROR_INVALID_ID: An object id is unknown.
+ * @GTK_BUILDER_ERROR_INVALID_FUNCTION: A function could not be found. This often happens
+ *     when symbols are set to be kept private. Compiling code with -rdynamic or using the
+ *     `gmodule-export-2.0` pkgconfig module can fix this problem.
  *
  * Error codes that identify various errors that can occur while using
  * #GtkBuilder.
@@ -83,7 +86,8 @@ typedef enum
   GTK_BUILDER_ERROR_TEMPLATE_MISMATCH,
   GTK_BUILDER_ERROR_INVALID_PROPERTY,
   GTK_BUILDER_ERROR_INVALID_SIGNAL,
-  GTK_BUILDER_ERROR_INVALID_ID
+  GTK_BUILDER_ERROR_INVALID_ID,
+  GTK_BUILDER_ERROR_INVALID_FUNCTION
 } GtkBuilderError;
 
 GDK_AVAILABLE_IN_ALL
@@ -196,6 +200,19 @@ void         gtk_builder_add_callback_symbols    (GtkBuilder    *builder,
 GDK_AVAILABLE_IN_ALL
 GCallback    gtk_builder_lookup_callback_symbol  (GtkBuilder    *builder,
                                                  const gchar   *callback_name);
+GDK_AVAILABLE_IN_ALL
+GClosure *   gtk_builder_create_closure          (GtkBuilder    *builder,
+                                                  const char    *function_name,
+                                                  gboolean       swapped,
+                                                  GObject       *object,
+                                                  GError       **error);
+GDK_AVAILABLE_IN_ALL
+GClosure *   gtk_builder_create_cclosure         (GtkBuilder    *builder,
+                                                  const char    *function_name,
+                                                  gboolean       swapped,
+                                                  GObject       *object,
+                                                  GError       **error);
+
 
 
 /**